﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.IO.Ports;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
using IPC.Communication.Framework.Core;
using IPC.Communication.Framework.LogNet;
using IPC.Communication.Framework.Reflection;

namespace IPC.Communication.Framework.Serial
{
    public class SerialBase : IDisposable
    {
        protected bool LogMsgFormatBinary = true;

        private bool disposedValue = false;

        protected SerialPort sP_ReadData = null;

        private SimpleHybirdLock hybirdLock;

        private ILogNet logNet;

        private int receiveTimeout = 5000;

        private int sleepTime = 20;

        private bool isClearCacheBeforeRead = false;

        private int connectErrorCount = 0;

        public ILogNet LogNet
        {
            get
            {
                return logNet;
            }
            set
            {
                logNet = value;
            }
        }

        [HslMqttApi(Description = "Gets or sets a value indicating whether the request sending (RTS) signal is enabled in serial communication.")]
        public bool RtsEnable
        {
            get
            {
                return sP_ReadData.RtsEnable;
            }
            set
            {
                sP_ReadData.RtsEnable = value;
            }
        }

        [HslMqttApi(Description = "Timeout for receiving data, default is 5000ms")]
        public int ReceiveTimeout
        {
            get
            {
                return receiveTimeout;
            }
            set
            {
                receiveTimeout = value;
            }
        }

        [HslMqttApi(Description = "Continuous serial port buffer data detection interval, the default 20ms, the smaller the value, the faster the communication, but the more unstable.")]
        public int SleepTime
        {
            get
            {
                return sleepTime;
            }
            set
            {
                if (value > 0)
                {
                    sleepTime = value;
                }
            }
        }

        [HslMqttApi(Description = "Whether to empty the buffer before sending data, the default is false")]
        public bool IsClearCacheBeforeRead
        {
            get
            {
                return isClearCacheBeforeRead;
            }
            set
            {
                isClearCacheBeforeRead = value;
            }
        }

        [HslMqttApi(Description = "The port name of the current connection serial port information")]
        public string PortName
        {
            get;
            private set;
        }

        [HslMqttApi(Description = "Baud rate of current connection serial port information")]
        public int BaudRate
        {
            get;
            private set;
        }

        public SerialBase()
        {
            sP_ReadData = new SerialPort();
            hybirdLock = new SimpleHybirdLock();
        }

        public virtual void SerialPortInni(string portName)
        {
            SerialPortInni(portName, 9600);
        }

        public virtual void SerialPortInni(string portName, int baudRate)
        {
            SerialPortInni(portName, baudRate, 8, StopBits.One, Parity.None);
        }

        public virtual void SerialPortInni(string portName, int baudRate, int dataBits, StopBits stopBits, Parity parity)
        {
            if (!sP_ReadData.IsOpen)
            {
                sP_ReadData.PortName = portName;
                sP_ReadData.BaudRate = baudRate;
                sP_ReadData.DataBits = dataBits;
                sP_ReadData.StopBits = stopBits;
                sP_ReadData.Parity = parity;
                PortName = sP_ReadData.PortName;
                BaudRate = sP_ReadData.BaudRate;
            }
        }

        public void SerialPortInni(Action<SerialPort> initi)
        {
            if (!sP_ReadData.IsOpen)
            {
                SerialPortInni("COM1");
                initi(sP_ReadData);
                PortName = sP_ReadData.PortName;
                BaudRate = sP_ReadData.BaudRate;
            }
        }

        public OperateResult Open()
        {
            try
            {
                if (!sP_ReadData.IsOpen)
                {
                    sP_ReadData.Open();
                    return InitializationOnOpen();
                }
                return OperateResult.CreateSuccessResult();
            }
            catch (Exception ex)
            {
                if (connectErrorCount < 100000000)
                {
                    connectErrorCount++;
                }
                return new OperateResult(-connectErrorCount, ex.Message);
            }
        }

        public bool IsOpen()
        {
            return sP_ReadData.IsOpen;
        }

        public void Close()
        {
            if (sP_ReadData.IsOpen)
            {
                ExtraOnClose();
                sP_ReadData.Close();
            }
        }

        [HslMqttApi(Description = "The raw byte data is sent to the serial port, and then a piece of data is received from the serial port.")]
        public OperateResult<byte[]> ReadFromCoreServer(byte[] send)
        {
            return ReadFromCoreServer(send, hasResponseData: true);
        }

        protected virtual byte[] PackCommandWithHeader(byte[] command)
        {
            return command;
        }

        protected virtual OperateResult<byte[]> UnpackResponseContent(byte[] send, byte[] response)
        {
            return OperateResult.CreateSuccessResult(response);
        }

        public OperateResult<byte[]> ReadFromCoreServer(byte[] send, bool hasResponseData, bool usePackAndUnpack = true)
        {
            byte[] array = usePackAndUnpack ? PackCommandWithHeader(send) : send;
            if (LogNet != null)
            {
                LogNet.WriteDebug(ToString(), StringResources.Language.Send + " : " + (LogMsgFormatBinary ? array.ToHexString(' ') : Encoding.ASCII.GetString(array)));
            }
            hybirdLock.Enter();
            OperateResult operateResult = Open();
            if (!operateResult.IsSuccess)
            {
                hybirdLock.Leave();
                return OperateResult.CreateFailedResult<byte[]>(operateResult);
            }
            if (IsClearCacheBeforeRead)
            {
                ClearSerialCache();
            }
            OperateResult operateResult2 = SPSend(sP_ReadData, array);
            if (!operateResult2.IsSuccess)
            {
                hybirdLock.Leave();
                return OperateResult.CreateFailedResult<byte[]>(operateResult2);
            }
            if (!hasResponseData)
            {
                hybirdLock.Leave();
                return OperateResult.CreateSuccessResult(new byte[0]);
            }
            OperateResult<byte[]> operateResult3 = SPReceived(sP_ReadData, awaitData: true);
            hybirdLock.Leave();
            if (!operateResult3.IsSuccess)
            {
                return operateResult3;
            }
            if (LogNet != null)
            {
                LogNet.WriteDebug(ToString(), StringResources.Language.Receive + " : " + (LogMsgFormatBinary ? operateResult3.Content.ToHexString(' ') : Encoding.ASCII.GetString(operateResult3.Content)));
            }
            return usePackAndUnpack ? UnpackResponseContent(array, operateResult3.Content) : operateResult3;
        }

        public OperateResult<byte[]> ClearSerialCache()
        {
            return SPReceived(sP_ReadData, awaitData: false);
        }

        //[AsyncStateMachine(typeof(<ReadFromCoreServerAsync>d__13))]
        //[DebuggerStepThrough]
        //public Task<OperateResult<byte[]>> ReadFromCoreServerAsync(byte[] value)
        //{
        //    <ReadFromCoreServerAsync>d__13 stateMachine = new <ReadFromCoreServerAsync>d__13();
        //    stateMachine.<>t__builder = AsyncTaskMethodBuilder<OperateResult<byte[]>>.Create();
        //    stateMachine.<>4__this = this;
        //    stateMachine.value = value;
        //    stateMachine.<>1__state = -1;
        //    stateMachine.<>t__builder.Start(ref stateMachine);
        //    return stateMachine.<>t__builder.Task;
        //}

        protected virtual OperateResult InitializationOnOpen()
        {
            return OperateResult.CreateSuccessResult();
        }

        protected virtual OperateResult ExtraOnClose()
        {
            return OperateResult.CreateSuccessResult();
        }

        protected virtual OperateResult SPSend(SerialPort serialPort, byte[] data)
        {
            if (data != null && data.Length != 0)
            {
                try
                {
                    serialPort.Write(data, 0, data.Length);
                    return OperateResult.CreateSuccessResult();
                }
                catch (Exception ex)
                {
                    if (connectErrorCount < 100000000)
                    {
                        connectErrorCount++;
                    }
                    return new OperateResult(-connectErrorCount, ex.Message);
                }
            }
            return OperateResult.CreateSuccessResult();
        }

        protected virtual OperateResult<byte[]> SPReceived(SerialPort serialPort, bool awaitData)
        {
            byte[] array = new byte[1024];
            MemoryStream memoryStream = new MemoryStream();
            DateTime now = DateTime.Now;
            while (true)
            {
                Thread.Sleep(sleepTime);
                try
                {
                    if (serialPort.BytesToRead >= 1)
                    {
                        int count = serialPort.Read(array, 0, array.Length);
                        memoryStream.Write(array, 0, count);
                        continue;
                    }
                    if ((DateTime.Now - now).TotalMilliseconds > (double)ReceiveTimeout)
                    {
                        memoryStream.Dispose();
                        if (connectErrorCount < 100000000)
                        {
                            connectErrorCount++;
                        }
                        return new OperateResult<byte[]>(-connectErrorCount, String.Format("Time out: {0}", ReceiveTimeout));
                    }
                    if (memoryStream.Length <= 0 && awaitData)
                    {
                        continue;
                    }
                }
                catch (Exception ex)
                {
                    memoryStream.Dispose();
                    if (connectErrorCount < 100000000)
                    {
                        connectErrorCount++;
                    }
                    return new OperateResult<byte[]>(-connectErrorCount, ex.Message);
                }
                break;
            }
            byte[] value = memoryStream.ToArray();
            connectErrorCount = 0;
            return OperateResult.CreateSuccessResult(value);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    if (hybirdLock != null)
                    {
                        hybirdLock.Dispose();
                    }
                    if (sP_ReadData != null)
                    {
                        sP_ReadData.Dispose();
                    }
                }
                disposedValue = true;
            }
        }

        public void Dispose()
        {
            Dispose(disposing: true);
        }

        public override string ToString()
        {
            return String.Format("SerialBase[{0},{1},{2},{3},{4}]", sP_ReadData.PortName, sP_ReadData.BaudRate, sP_ReadData.DataBits, sP_ReadData.StopBits, sP_ReadData.Parity);
        }
    }
}
